package com.pl.config;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池的相关配置
 *
 * @author shent
 * <p>
 */
@EnableAsync
@Configuration
public class ThreadPoolConfig {
    /**
     * Nthreads=Ncpu*Ucpu*(1+w/c)，其中
     * Nthreads核心线程数
     * Ncpu=CPU核心数
     * Ucpu=cpu使用率，0~1
     * W/C=等待时间与计算时间的比率
     * 或
     * 核心线程数=CPU可用核心数/(1-阻塞系数),其中
     * 阻塞系数在 0 和 1 之间
     * <p>
     * 那么实际使用中并发线程数如何设置呢？
     * Nthreads=Ncpu*(1+w/c)
     * IO密集型：一般情况下，如果存在IO，那么肯定w/c>1（阻塞耗时一般都是计算耗时的很多倍）,但是需要考虑系统内存有限（每开启一个线程都需要内存空间），这里需要上服务器测试具体多少个线程数适合（CPU占比、线程数、总耗时、内存消耗）。
     * 如果不想去测试，保守点取1即，Nthreads=Ncpu*(1+1)=2Ncpu。这样设置一般都OK。
     * 计算密集型：假设没有等待w=0，则W/C=0. Nthreads=Ncpu。
     * <p>
     * ThreadPoolTaskExecutor 的阻塞队列
     * 当 QueueCapacity>0 时,阻塞队列为 LinkedBlockingQuene：基于链表结构的阻塞队列，按FIFO排序任务，吞吐量通常要高于ArrayBlockingQuene；
     * 当 QueueCapacity<=0 时,阻塞队列为 SynchronousQuene：一个不存储元素的阻塞队列，每个插入操作必须等到另一个线程调用移除操作，否则插入操作一直处于阻塞状态，吞吐量通常要高于LinkedBlockingQuene；
     *
     * @return
     */
    @Bean("MyThreadPool")
    public ThreadPoolTaskExecutor myThreadPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("myThreadPool");
        // 获取服务器核心数
        int processorCount = Runtime.getRuntime().availableProcessors();
        executor.setCorePoolSize(2 * processorCount);
        // 设置阻塞队列大小,默认为Integer.MAX_VALUE
        executor.setQueueCapacity(16);
        // 被拒绝任务的处理程序，它直接在execute方法的调用线程中运行被拒绝的任务，除非执行程序已关闭，在这种情况下任务将被丢弃
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置线程池最大线程数,默认为Integer.MAX_VALUE
        // 最大线程数=核心线程数+阻塞队列大小+非核心线程数(自己定义)
        executor.setMaxPoolSize(2 * processorCount + 16 + 8);
        executor.initialize();
        return executor;
    }

    /**
     * 用于解决线程池中异步线程
     * 的异常处理问题
     *
     * @return
     */
    @Bean
    public AsyncConfigurer configurer() {
        return new AsyncConfigurer() {
            @Override
            public Executor getAsyncExecutor() {
                return myThreadPool();
            }

            @Override
            public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
                return (ex, method, params) -> {
                    //具体处理异常的措施
                    System.out.println(ex);
                    System.out.println("==============================================================================");
                    System.out.println(Thread.currentThread().getName() + " has some error!");
                    System.out.println("==============================================================================");
                };
            }
        };
    }

 /*@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    public AsyncAnnotationBeanPostProcessor myAsyncAdvisor() {
        AsyncAnnotationBeanPostProcessor asyncAdvisor = new AsyncAnnotationBeanPostProcessor();
        asyncAdvisor.setExceptionHandler((ex, method, params) -> {
            System.out.println("==============================================================================");
            System.out.println("@@#"+ex);
            System.out.println("==============================================================================");
            System.out.println(Thread.currentThread().getName() + " has some error!");
            System.out.println("==============================================================================");
        });
        return asyncAdvisor;
    }*/
}
